/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *                  The shell acts as a task running in user mode.
 *       The main function is to make system calls through the user's output.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

#define SHELL_BEGIN 20
#define CMD_MAX 5
void cmd_ps(int argc, char *argv[]);
int handle_input(char *buff, int buff_size);
void handle_argc(char *buff, int buff_size, int *argc, char *argv[]);
void handle_command(int argc, char *argv[]);

char lastInput [32];
int lastLen;
typedef struct
{
    char name[32];
    void (*func)(int argc, char *argv[]);
} command_t;

void cmd_ps(int argc, char *argv[])
{
    sys_ps();
}

void cmd_clear(int argc, char *argv[])
{
    sys_clear();
    sys_move_cursor(0, SHELL_BEGIN);
    printf("------------------- COMMAND -------------------\n");
    printf("> root@UCAS_OS: ");
}
void cmd_exec(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("ERROR:parm is not enough\n");
        return;
    }
    int is_wait = 1;
    if (argv[2] != 0)
    {
        is_wait = (strcmp(argv[2], "&") != 0);
    }
    char *name = argv[1];
    int real_argc = argc - 1;
    char **real_argv = argv + 1;
    int pid = sys_exec(name, real_argc, real_argv);
    if (pid == 0)
        printf("ERROR:proc %s is wrong when try to begin it", name);
    if (is_wait)
    {
        sys_waitpid(pid);
    }
    return pid;
}
void cmd_kill(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("ERROR:parm is not enough\n");
        return;
    }
    int pid = atoi(argv[1]);
    if (!sys_kill(pid))
    {
        printf("ERROR:kill is failed\n");
    }
    else
    {
        printf("SUCCEED:kill %d proc\n", pid);
    }
}
void cmd_taskset(int argc, char *argv[])
{
    if (argc == 3)
    {
        char *name = argv[2];
        int real_argc = argc - 1;
        char **real_argv = argv + 1;
        sys_exec(name, real_argc, real_argv);
    }
    else if(argc== 4)
    {
        int mask = atoi(argv[2]);
        int pid = atoi(argv[3]);
        sys_taskset(mask,pid);
    }
}
command_t command_dir[100] = {
    {"ps", &cmd_ps},
    {"cl", &cmd_clear},
    {"exec", &cmd_exec},
    {"kill", &cmd_kill},
    {"taskset", &cmd_taskset},};

int handle_input(char *buff, int buff_size)
{
    printf("$");
    memset(buff, 0, buff_size);
    char *end = buff;
    for (;;)
    {
        int ch = -1;
        while (ch == -1)
        {
            ch = sys_getchar();
        }

        if (ch > 0)
        {
            if (ch == '\r' || ch == '\n')
            {
                *end = 0;
                printf("\n");
                break;
            }
            else if (ch == 8 || ch == 127)
            {
                if (end > buff)
                {
                    printf("\b");
                    end--;
                }
            }
            // else if(ch == 37)//上箭头
            // {
            //     memset(buff,0,100);
            //     end = buff;
            //     memcpy(buff,lastInput,lastLen);
            //     end += lastLen;
            //     printf("%s",buff);
            // }
            else
            {
                printf("%c", ch);
                char p = ch;
                *end = p;
                end++;
            }
        }
    }
    return end - buff;
}
void handle_argc(char *buff, int buff_size, int *argc, char *argv[])
{
    *argc = 0;
    int is_empty = 1;
    for (int i = 0; i < buff_size && buff[i] != 0; i++)
    {
        if (buff[i] == 32)
        {
            is_empty = 1;
            buff[i] = 0;
        }
        else
        {
            if (is_empty)
            {
                argv[(*argc)] = buff + i;
                (*argc)++;
            }
            is_empty = 0;
        }
    }
    return;
}
void handle_command(int argc, char *argv[])
{
    for (int i = 0; i < CMD_MAX; i++)
    {
        if (strcmp(command_dir[i].name, argv[0]) == 0)
        {
            command_dir[i].func(argc, argv);
            return;
        }
    }
    printf("Unknown command %s\n", argv[0]);
    return;
}

void init_command()
{
    command_dir[0].func = &cmd_ps;
    command_dir[1].func = &cmd_clear;
    command_dir[2].func = &cmd_exec;
    command_dir[3].func = &cmd_kill;
    command_dir[4].func = &cmd_taskset;
}

int main(void)
{
    char buff[100];
    int argc;
    char *argv[100];
    cmd_clear(argc, argv);

    init_command();

    while (1)
    {
        // TODO [P3-task1]: call syscall to read UART port

        // TODO [P3-task1]: parse input
        // note: backspace maybe 8('\b') or 127(delete)
        int len = -1;
        if ((len = handle_input(buff, sizeof(buff))) <= 0)
        {
            continue;
        }
        memcpy(lastInput,buff,len);
        lastLen = len;
        handle_argc(buff, len, &argc, argv);
        handle_command(argc, argv);
        //  TODO [P3-task1]: ps, exec, kill, clear
    }

    return 0;
}
